﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace ICeFramework.Utilities.Drawing
{

    // Source:
    //  http://www.vbaccelerator.com/home/NET/Code/Libraries/Graphics/HLS_to_RGB/Hlsrgb_zip_HLSRGB_HLSRGBVB_HLSRGB_vb.asp

    public class HlsRgbUtility
    {

        private byte m_red = 0;
        private byte m_green = 0;
        private byte m_blue = 0;

        private double m_hue = 0;
        private double m_luminance = 0;
        private double m_saturation = 0;

        public HlsRgbUtility(byte red, byte green, byte blue)
        {
            m_red = red;
            m_green = green;
            m_blue = blue;
        }

        public HlsRgbUtility(HlsRgbUtility hlsrgb)
        {
            m_red = hlsrgb.Red;
            m_blue = hlsrgb.Blue;
            m_green = hlsrgb.Green;
            m_luminance = hlsrgb.Luminance;
            m_hue = hlsrgb.Hue;
            m_saturation = hlsrgb.Saturation;
        }

        public HlsRgbUtility(Color c)
        {
            m_red = c.R;
            m_green = c.G;
            m_blue = c.B;
            ToHLS();
        }

        public HlsRgbUtility(string htmlColor)
            : this(ColorTranslator.FromHtml(htmlColor))
        {
        }

        private HlsRgbUtility()
        {
        }

        public byte Red
        {
            get { return m_red; }
            set { m_red = value; ToHLS(); }
        }

        public byte Green
        {
            get { return m_green; }
            set { m_green = value; ToHLS(); }
        }

        public byte Blue
        {
            get { return m_blue; }
            set { m_blue = value; ToHLS(); }
        }

        public double Luminance
        {
            get { return m_luminance; }
            set
            {
                if (((value < 0f) | (value > 1f)))
                {
                    throw new ArgumentOutOfRangeException("Luminance", "Luminance must be between 0.0 and 1.0");
                }
                m_luminance = value;
                ToRGB();
            }
        }

        public double Hue
        {
            get { return m_hue; }
            set
            {
                if (((value < 0f) | (value > 360f)))
                {
                    throw new ArgumentOutOfRangeException("Hue", "Hue must be between 0.0 and 360.0");
                }
                m_hue = value;
                ToRGB();
            }
        }

        public double Saturation
        {
            get { return m_saturation; }
            set
            {
                if (((value < 0f) | (value > 1f)))
                {
                    throw new ArgumentOutOfRangeException("Saturation", "Saturation must be between 0.0 and 1.0");
                }
                m_saturation = value;
                ToRGB();
            }
        }

        public Color Color
        {
            get { return Color.FromArgb(m_red, m_green, m_blue); }
            set
            {
                m_red = value.R;
                m_green = value.G;
                m_blue = value.B;
                ToHLS();
            }
        }

        public void LightenColor(double lightenBy)
        {
            m_luminance *= (1f + lightenBy);
            if ((m_luminance > 1f))
            {
                m_luminance = 1f;
            }
            ToRGB();
        }

        public void DarkenColor(double darkenBy)
        {
            m_luminance *= darkenBy;
            ToRGB();
        }

        public HlsRgbUtility(double hue, double luminance, double saturation)
        {
            if (((saturation < 0f) | (saturation > 1f)))
            {
                throw new ArgumentOutOfRangeException("Saturation", "Saturation must be between 0.0 and 1.0");
            }
            if (((hue < 0f) | (hue > 360f)))
            {
                throw new ArgumentOutOfRangeException("Hue", "Hue must be between 0.0 and 360.0");
            }
            if (((luminance < 0f) | (luminance > 1f)))
            {
                throw new ArgumentOutOfRangeException("Luminance", "Luminance must be between 0.0 and 1.0");
            }
            m_hue = hue;
            m_luminance = luminance;
            m_saturation = saturation;
            ToRGB();
        }

        private void ToHLS()
        {
            byte minval = Math.Min(m_red, Math.Min(m_green, m_blue));
            byte maxval = Math.Max(m_red, Math.Max(m_green, m_blue));
            double mdiff = (maxval * 1.0 - minval * 1.0);
            double msum = (maxval * 1.0 + minval * 1.0);
            m_luminance = msum / 510f;
            if ((maxval == minval))
            {
                m_saturation = 0f;
                m_hue = 0f;
            }
            else
            {
                double rnorm = (maxval - m_red) / mdiff;
                double gnorm = (maxval - m_green) / mdiff;
                double bnorm = (maxval - m_blue) / mdiff;
                if ((m_luminance <= 0.5f))
                {
                    m_saturation = (mdiff / msum);
                }
                else
                {
                    m_saturation = (mdiff / (510f - msum));
                }
                if ((m_red == maxval))
                {
                    m_hue = 60f * (6f + bnorm - gnorm);
                }
                if ((m_green == maxval))
                {
                    m_hue = 60f * (2f + rnorm - bnorm);
                }
                if ((m_blue == maxval))
                {
                    m_hue = 60f * (4f + gnorm - rnorm);
                }
                if ((m_hue > 360f))
                {
                    m_hue = m_hue - 360f;
                }
            }
        }

        private void ToRGB()
        {
            if ((m_saturation == 0.0))
            {
                m_red = Convert.ToByte(m_luminance * 255f);
                m_green = m_red;
                m_blue = m_red;
            }
            else
            {
                double rm1;
                double rm2;
                if ((m_luminance <= 0.5f))
                {
                    rm2 = m_luminance + m_luminance * m_saturation;
                }
                else
                {
                    rm2 = m_luminance + m_saturation - m_luminance * m_saturation;
                }
                rm1 = 2f * m_luminance - rm2;
                m_red = ToRGB1(rm1, rm2, m_hue + 120f);
                m_green = ToRGB1(rm1, rm2, m_hue);
                m_blue = ToRGB1(rm1, rm2, m_hue - 120f);
            }
        }

        private byte ToRGB1(double rm1, double rm2, double rh)
        {
            if ((rh > 360f))
            {
                rh -= 360f;
            }
            else if ((rh < 0f))
            {
                rh += 360f;
            }
            if ((rh < 60f))
            {
                rm1 = rm1 + (rm2 - rm1) * rh / 60f;
            }
            else if ((rh < 180f))
            {
                rm1 = rm2;
            }
            else if ((rh < 240f))
            {
                rm1 = rm1 + (rm2 - rm1) * (240f - rh) / 60f;
            }
            return Convert.ToByte(rm1 * 255);
        }
    }
}
